package iagovsothello.ai;

import iagovsothello.core.GameState;
import iagovsothello.core.Position;
import iagovsothello.logger.Entry;
import iagovsothello.logger.Log;

import java.util.LinkedList;
import java.util.List;

public class Minimax extends TreeSearchAgent {

	public static final String name = "Minimax";
	private static final long serialVersionUID = 834202790355914703L;
	
	public Minimax()
	{
		super();
	}
	
	public Minimax(int playerNumber) 
	{
		super(playerNumber);
	}

	public Minimax(int playerNumber, boolean debugMode) 
	{
		super(playerNumber, debugMode);
	}

	public Minimax(int playerNumber, boolean debugMode, Log log)
	{
		super(playerNumber, debugMode, log);
	}

	public String getName()
	{
		return name;
	}
	
	@Override
	public TreeSearchAgent clone() 
	{
		Minimax copy = new Minimax();
		copyTreeSearchAgentAttributes(copy);
		return copy;
	}

	public String toString()
	{
		return getName();
	}
	
	protected int exploreNode(Node node, int depth, Entry e)
	{
//		for (int i = 0; i < node.state.getTurnNumber(); i++)
//		{
//			System.out.print("-");
//		}
//		System.out.println(node.state.getTurnNumber()+": "+node.state);
		
		List<Position> list = node.state.getLegalPositions();
		if (list.isEmpty() && node.state.isGameOver())
		{
			if (e != null && e.maxDepth < depth)
				e.maxDepth = depth;
			
			node.terminal = true;
			node.sequence = new LinkedList<Position>();
			node.sequence.add(node.move);
			return utilityValue(node.state);
		}
		else
		{
			//controllo in tabella hash
			if (hashTable != null)
			{
				Integer v = getFromTable(node.state);
				if (v != null)
				{
					return v;
				}
			}
			
			/*se questo nodo non possiede figli, allora vengono creati esplorando le mosse disponibili, altrimenti si prosegue con i figli già presenti*/
			if (node.children == null)
			{
				node.children = new LinkedList<Node>();
				if (list.isEmpty())
				{
					node.children.add(new Node(node.state.cloneAndMove(null),null));
				}
				else
				{
					for (Position p : list)
					{
						node.children.add(new Node(node.state.cloneAndMove(p),p));
					}
				}
			}
			if (e != null)
			{
//				e.nodes += node.children.size();
				if (depth == 0)
					e.branchingFactor = node.children.size();
			}
			
			//valore minimax del nodo
			int v;
			
			//se il nodo corrente è un nodo MAX
			if (node.state.isMyTurn(playerNumber))
			{
				v = maxValue(node,depth,e);
			}
			//se il nodo corrente è invece un nodo MIN
			else
			{
				v = minValue(node,depth,e);
			}
			
			//se la tabella esiste e si può aggiungere un nuovo record, si aggiunge in tabella 
			if (hashTable != null && this.hashTableRewriting && hashTable.size() < hashTableMaxDimension)
			{
				this.updateTable(node.state, v);
			}
			return v;
		}	
	}
	
	private int maxValue(Node node, int depth, Entry e)
	{
		int minimaxValue = Integer.MIN_VALUE;
		LinkedList<Position> optimumSequence = null;
		//System.out.println("size:" + children.size());

		for (Node n : node.children)
		{
			int r = exploreNode(n,depth+1,e);
			if (r > minimaxValue)
			{
				minimaxValue = r;
				optimumSequence = n.sequence;
			}
			n.childrenDestroy();
		}
		if (optimumSequence == null)
		{
			node.sequence = new LinkedList<Position>();
			node.sequence.add(node.move);
		}
		else
		{
			node.sequence = optimumSequence;
			node.sequence.addFirst(node.move);
		}
		return minimaxValue;
	}
	
	private int minValue(Node node, int depth, Entry e)
	{
		int minimaxValue = Integer.MAX_VALUE;
		LinkedList<Position> optimumSequence = null;
		for (Node n : node.children)
		{
			int r = exploreNode(n,depth+1,e);
			if (r < minimaxValue)
			{
				minimaxValue = r;
				optimumSequence = n.sequence;
			}
			n.childrenDestroy();
		}
		if (optimumSequence == null)
		{
			node.sequence = new LinkedList<Position>();
			node.sequence.add(node.move);
		}
		else
		{
			node.sequence = optimumSequence;
			node.sequence.addFirst(node.move);
		}
		return minimaxValue;
	}
}
